//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//

#if defined(OEM2_DRIVER)
#define DELAYLD_NO_GETPROCFROMDLL
#include "precomp.h"
#include "simmgr.h"
#include "rilndisoem2.h"
#include <Delayld.h>

DWORD WINAPI NdisNotifyThreadProc(LPVOID lpParameter);
#define MAX_NDIS_EVENT_NAME 100
#define MAX_IP_ADDRESS_LENGTH 4

HINSTANCE g_hinstOEMNDIS /*= NULL*/;
CRilNDIS* g_pCRilNdis = NULL;

#define OEM_LIBRARY TEXT("ttpmux")
#define OEM_NdisInitialize TEXT("ttpcomNdisInitialise")
#define OEM_NdisShutdown   TEXT("ttpcomNdisShutdown")
#define OEM_NdisReceiveEvent   TEXT("ttpcomNdisReceiveEvent")
#define OEM_NdisTransmitEvent  TEXT("ttpcomNdisTransmitEvent")

typedef HANDLE  (*PFN_NDIS_INITIALIZE)(DWORD);
typedef BOOL    (*PFN_NDIS_SHUTDOWN)(HANDLE);
typedef BOOL    (*PFN_NDIS_RECEIVE_EVENT)(HANDLE, OEMNDISEVENT*,BOOL*);
typedef BOOL    (*PFN_NDIS_TRANSMIT_EVENT)(HANDLE, const OEMNDISEVENT*);

PFN_NDIS_INITIALIZE      g_pfnNdisInitialize;
PFN_NDIS_SHUTDOWN        g_pfnNdisShutdown;
PFN_NDIS_RECEIVE_EVENT   g_pfnNdisReceiveEvent;
PFN_NDIS_TRANSMIT_EVENT  g_pfnNdisTransmitEvent;


HINSTANCE LoadPacketLib()
{
    if (!g_hinstOEMNDIS)
    {
        g_hinstOEMNDIS = LoadLibrary(OEM_LIBRARY);
        ASSERT(g_hinstOEMNDIS);
    }
    return g_hinstOEMNDIS;
}

HRESULT GetPacketInterface()
{
    HRESULT hr = E_FAIL;

    HINSTANCE hinst;
    hinst = LoadPacketLib();
    if (!hinst)
    {
        ASSERT(0);
        goto Error;
    }

    g_pfnNdisInitialize =    (PFN_NDIS_INITIALIZE)GetProcAddress(hinst, OEM_NdisInitialize );
    g_pfnNdisShutdown =      (PFN_NDIS_SHUTDOWN)GetProcAddress(hinst, OEM_NdisShutdown );
    g_pfnNdisReceiveEvent =  (PFN_NDIS_RECEIVE_EVENT)GetProcAddress(hinst, OEM_NdisReceiveEvent);
    g_pfnNdisTransmitEvent = (PFN_NDIS_TRANSMIT_EVENT)GetProcAddress(hinst, OEM_NdisTransmitEvent);
    if (!g_pfnNdisInitialize || !g_pfnNdisShutdown || !g_pfnNdisReceiveEvent || !g_pfnNdisTransmitEvent)
    {
        ASSERT(0);
        goto Error;
    }
    else
    {
        hr = S_OK;
    }

Error:
    return hr;
}

BOOL ValidChannelIndex ( DWORD dwIndex )
{
    return ((dwIndex >= FIRST_VALID_NDIS_INDEX) && (dwIndex <= MAX_OEMNDIS_CHANNELS)) ? TRUE : FALSE;
    
}

// **************************************************************************
// Function Name:  myinet_addr
// 
// Purpose: Convert string IP address into a DWORD
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************

DWORD myinet_addr(const UCHAR *lpString, UCHAR length)
{
  DWORD dwIpAddr = 0;
  
  if ( length > MAX_IP_ADDRESS_LENGTH  )
    length = MAX_IP_ADDRESS_LENGTH;
     
  for(int i=0; i<length; i++)
  {
    if ( i )
      dwIpAddr <<= 8;
    dwIpAddr |= lpString[i];

  }
    
  return dwIpAddr;
}

// **************************************************************************
// Function Name:  myinet_ipv6addr
//
// Purpose: Convert string IPV6 address into a RILNDISIPV6ADDR
//
// Arguments:
//
// Return Values: RILNDISIPV6ADDR struct
//
// Side effects:
//
// Description:
// **************************************************************************
RILNDISIPV6ADDR myinet_ipv6addr(const UCHAR *lpString, UCHAR length)
{
    RILNDISIPV6ADDR ipv6addr;

    memset( &ipv6addr, 0, sizeof(ipv6addr) );

    if ( length == IPV6_ADDRESS_LENGTH )
    {
        memcpy( ipv6addr.Byte, lpString, (IPV6_ADDRESS_LENGTH_IN_UCHAR*sizeof(UCHAR)) );
    }

    return ipv6addr;
}

// **************************************************************************
// Function Name:  NdisNotifyThreadProc
// 
// Purpose:        Process NDIS events from radio.
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
DWORD WINAPI NdisNotifyThreadProc(LPVOID lpParameter)
{
    CRilHandle* pRilDevice = (CRilHandle*)lpParameter;
    DWORD dwResult;

    if (!CeSetThreadPriority(GetCurrentThread(), 116))
    {
        return 0;
    }

    dwResult = pRilDevice->CRilNdis.NdisNotifyThread();

    return dwResult;
}


#if defined DEBUG
BOOL NdisPacketTest ( RILNDISPACKET* lpNdisPacket )
{
    BOOL fReturn = FALSE;

    RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisPacketTest Packet = %x \r\n"), lpNdisPacket));    

    if ( lpNdisPacket )
    {
        if ( lpNdisPacket->cBufferCount )
        {
            RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisPacketTest cBufferCount = %d \r\n"), lpNdisPacket->cBufferCount));
            LPRILNDISBUFFER lpRilNdisBuffer = lpNdisPacket->NDISBuffer;
            DWORD dwBufferCount=0;
            for(DWORD i=0; i<lpNdisPacket->cBufferCount;i++)
            {
                RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisPacketTest NDISBuffer[%d] = %x \r\n"), i, lpRilNdisBuffer));
                if ( lpRilNdisBuffer )
                {
                    RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisPacketTest NDISBuffer[%d].pbyBytes = %x, cByteCount = %x \r\n"), i, lpRilNdisBuffer->pbyBytes,lpRilNdisBuffer->cByteCount));
                    if (lpRilNdisBuffer->pbyBytes /*&& lpRilNdisBuffer->cByteCount*/)
                    {
                        /* Apparently we can get buffers without any data. Don't fail the packet test if this is true */
                        dwBufferCount++;
                        lpRilNdisBuffer++;
                        continue;
                    }
                    break;
                }
            }
            if ( dwBufferCount == lpNdisPacket->cBufferCount )
            {
                fReturn = TRUE;
            }
        }
    }
    ASSERT(fReturn);
    return ( fReturn );
}

void DumpPacket (RILNDISPACKET* lpPacket)
{
    if (ZONE_NDISPKTS)
    {
        if ( lpPacket )
        {
            LPRILNDISBUFFER lpRilNdisBuffer = NULL;
            wchar_t wLineBuf[256];
                    
                           
            RETAILMSG(MSG_ON, (TEXT("RILNDIS DumpPacket lpPacket = %x, dwSize = %x, dwContextId = %x, cBufferCount = %x,\r\n"), 
                             lpPacket, 
                             lpPacket->dwSize, 
                             lpPacket->dwContextId,
                             lpPacket->cBufferCount));  
                             
               
           lpRilNdisBuffer = lpPacket->NDISBuffer;

           for(DWORD i=0; i<lpPacket->cBufferCount; i++)
           {
                RETAILMSG(MSG_ON, (TEXT("RILNDIS: Dump Packet Buffer = %0x cByteCount = %0x\r\n"), lpRilNdisBuffer,  lpRilNdisBuffer->cByteCount));
                
                wcscpy(wLineBuf,L"");
                for(DWORD x=0; x<lpRilNdisBuffer->cByteCount; x++)
                {
                    if ( lpRilNdisBuffer->pbyBytes[x] < 0x10 )
                    {
                         _itow(0, &wLineBuf[wcslen(wLineBuf)], 16 );
                         _itow(lpRilNdisBuffer->pbyBytes[x], &wLineBuf[wcslen(wLineBuf)], 16);
                    }
                    else
                    {
                         _itow(lpRilNdisBuffer->pbyBytes[x], &wLineBuf[wcslen(wLineBuf)], 16 );
                    }
                        
                    wcscat(&wLineBuf[wcslen(wLineBuf)], L" ");
                    if ( wcslen(wLineBuf) > 45)
                    {
                        RETAILMSG(MSG_ON,(TEXT("%s\r\n"),wLineBuf));
                        wcscpy(wLineBuf,L"");
                    }
                }
                if ( wcslen(wLineBuf))
                {
                    RETAILMSG(MSG_ON,(TEXT("%s\r\n"),wLineBuf));
                    wcscpy(wLineBuf,L"");
                }
                lpRilNdisBuffer++; 
           }
       }
    }
}

void DumpNdisIPConfig( RILNDISIPCONFIG* pRilNdisIpConfig )
{
    if ( pRilNdisIpConfig )
        {
        RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig cbSize = %x \r\n"),pRilNdisIpConfig->cbSize));
        RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig dwContextId = %x \r\n"),pRilNdisIpConfig->dwContextId));
        RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig dwProtocol = %x \r\n"),pRilNdisIpConfig->dwProtocol));
        RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.dwFlags = %x \r\n"),pRilNdisIpConfig->ipv4.dwFlags));
        RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.inIPAddress = %x \r\n"),pRilNdisIpConfig->ipv4.inIPAddress));
        RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.inPrimaryDNS = %x \r\n"),pRilNdisIpConfig->ipv4.inPrimaryDNS));
        RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.inSecondaryDNS = %x \r\n"),pRilNdisIpConfig->ipv4.inSecondaryDNS));
        RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.inDefaultGateway = %x \r\n"),pRilNdisIpConfig->ipv4.inDefaultGateway));
        RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpNdisIPConfig ipv4.inSubnetMask = %x \r\n"),pRilNdisIpConfig->ipv4.inSubnetMask));

        }
}

void DEBUG_TYPE( OEMNDISEVENTTYPE type)
{
    switch ( type )
    {
        case OEM_NDIS_NULL_EVENT:            /* no event available                          */
            RETAILMSG(MSG_ON, (TEXT("RILNDIS:  OEM_NDIS_NULL_EVENT\r\n")));
            break;
        case OEM_NDIS_RX_PACKET:             /* Received a packet                           */
            RETAILMSG(MSG_ON, (TEXT("RILNDIS:  OEM_NDIS_RX_PACKET\r\n")));
            break;
        case OEM_NDIS_TX_PACKET_COMPLETE:    /* TX packet done - buffer can be reused       */
            RETAILMSG(MSG_ON, (TEXT("RILNDIS:  OEM_NDIS_TX_PACKET_COMPLETE\r\n")));
            break;
        case OEM_NDIS_IP_CONFIGURATION:      /* NDIS configuration from network             */
            RETAILMSG(MSG_ON, (TEXT("RILNDIS:  OEM_NDIS_IP_CONFIGURATION\r\n")));
            break;
        case OEM_NDIS_XOFF:                  /* Please stop sending data to me              */
            RETAILMSG(MSG_ON, (TEXT("RILNDIS:  OEM_NDIS_XOFF\r\n")));
            break;
        case OEM_NDIS_XON:                   /* Restart sending data to me                  */
            RETAILMSG(MSG_ON, (TEXT("RILNDIS:  OEM_NDIS_XON\r\n")));
            break;
        case OEM_NDIS_INITIALISE_COMPLETE:   /*Initialise is now complete                   */
            RETAILMSG(MSG_ON, (TEXT("RILNDIS:  OEM_NDIS_INITIALISE_COMPLETE\r\n")));
            break;
        case OEM_NDIS_OPEN_COMPLETE:         /* Open is now complete                        */
            RETAILMSG(MSG_ON, (TEXT("RILNDIS:  OEM_NDIS_OPEN_COMPLETE\r\n")));
            break;
        case OEM_NDIS_CLOSE_COMPLETE:        /* Close is now complete                       */
            RETAILMSG(MSG_ON, (TEXT("RILNDIS:  OEM_NDIS_CLOSE_COMPLETE\r\n")));
            break;
        case OEM_NDIS_TX_PACKET:             /* Request to transmit a packet                */
            RETAILMSG(MSG_ON, (TEXT("RILNDIS:  OEM_NDIS_TX_PACKET\r\n")));
            break;
        case OEM_NDIS_RX_PACKET_COMPLETE:    /* RX packet complete - buffer can be reused   */
            RETAILMSG(MSG_ON, (TEXT("RILNDIS:  OEM_NDIS_RX_PACKET_COMPLETE\r\n")));
            break;
        case OEM_NDIS_OPEN_CONNECTION:       /* open the connection                         */
            RETAILMSG(MSG_ON, (TEXT("RILNDIS:  OEM_NDIS_OPEN_CONNECTION\r\n")));
            break;
        case OEM_NDIS_CLOSE_CONNECTION:      /* close the connection                        */
            RETAILMSG(MSG_ON, (TEXT("RILNDIS:  OEM_NDIS_CLOSE_CONNECTION\r\n")));
            break;
        case OEM_NDIS_REGISTRATION_STATUS:   /* gsm or gprs registration status change      */
            RETAILMSG(MSG_ON, (TEXT("RILNDIS:  OEM_NDIS_REGISTRATION_STATUS\r\n")));
            break;
        case OEM_NDIS_BUFFER_STATUS:         /* buffer state has changed or response       */
            RETAILMSG(MSG_ON, (TEXT("RILNDIS:  OEM_NDIS_BUFFER_STATUS\r\n")));
            break;
    }
}

void PacketPointers (RILNDISPACKET* lpPacket)
{
    if ( lpPacket )
    {
        LPRILNDISBUFFER pRilNdisBufferIn = NULL;
        
        RETAILMSG(MSG_ON, (TEXT("RILNDIS: PacketPointer  Packet = %0x, Count = %x\r\n"), lpPacket, lpPacket->cBufferCount));
        
        if ( lpPacket->cBufferCount )
        {
            pRilNdisBufferIn = lpPacket->NDISBuffer;
                
            for(DWORD i=0; i<lpPacket->cBufferCount; i++)
            {
                RETAILMSG(MSG_ON, (TEXT("RILNDIS: PacketPointer Buffer = %0x \r\n"), pRilNdisBufferIn->pbyBytes));
                pRilNdisBufferIn++;
            }
        }
    }
}
#else
#define PacketPointers(a)
#define DEBUG_TYPE(a)
#define DumpPacket(a)
#define NdisPacketTest(a)
#define DumpNdisIPConfig(a)
#endif

CRilNDIS::CRilNDIS()
{
    m_pCrilHandle = NULL;
    m_fXon = RIL_NDIS_XON;
    m_hNdisNotifyThread = NULL;
    g_pCRilNdis = this;
}

CRilNDIS::~CRilNDIS()
{
    m_pCrilHandle = NULL;
    g_pCRilNdis = NULL;
}

void CRilNDIS::Init(PVOID pHandle)
{
    m_pCrilHandle = pHandle;
}

// **************************************************************************
// Function Name: NdisStart
// 
// Purpose:       Initialize to a known state and start NDIS thread.
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
BOOL CRilNDIS::NdisStart ( HANDLE hCancelEvent )
{
    m_hNdisNotifyThread = NULL;
    m_dwNdisNotifyThreadID = 0;
    m_hCancelEvent = hCancelEvent;
    memset( m_rgpNdisChannels, 0, sizeof(m_rgpNdisChannels)); 
    m_hNdisNotifyThread = CreateThread(NULL, 0, NdisNotifyThreadProc, (LPVOID)m_pCrilHandle, 0, &m_dwNdisNotifyThreadID);
    if ( m_hNdisNotifyThread )
    {
        InitializeCriticalSection(&m_csNdisEventList);
        InitializeCriticalSection(&m_csConnectionList);
        InitializeCriticalSection(&m_PdpContextCriticalSection);
        InitializeListHead(&m_PdpContextPendingList);
        InitializeListHead(&m_PdpContextFreeList);

    }

    if ( FAILED(GetPacketInterface()))
    {
        RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisStart GetPacketInterface FAILED \r\n")));
        ASSERT(0);
    }

    return (m_hNdisNotifyThread ? TRUE : FALSE );
}

// **************************************************************************
// Function Name: NdisStop
// 
// Purpose:       Bring down to a known state. Wait for NDIS thread to terminate.
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void CRilNDIS::NdisStop( void )
{
    if ( m_hNdisNotifyThread )
    {
      (void)WaitForSingleObject(m_hNdisNotifyThread,10000);
      (void)CloseHandle(m_hNdisNotifyThread);
      m_hNdisNotifyThread = NULL;
      m_dwNdisNotifyThreadID = 0;
      DeleteCriticalSection(&m_csNdisEventList);
      DeleteCriticalSection(&m_csConnectionList);
      DeleteCriticalSection(&m_PdpContextCriticalSection);

    }
}

// **************************************************************************
// Function Name:   NdisCountEvents
// 
// Purpose:         Convenience routine. Count number of events in WaitForMultipleObjects list.
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
DWORD CRilNDIS::NdisCountEvents(void)
{
    DWORD dwCount=0;
    
    for(int i=0;i<MAX_OEMNDIS_EVENTS;i++)
    {
       if ( !m_rgpNdisEvents.lpNdisEventHandlesPrimary[i] )
         break;
       else
         dwCount++;
    }

    return dwCount;
}

// **************************************************************************
// Function Name:  NdisGetFreeNdisEntry
// 
// Purpose:        Find an unused NDIS channel.
//
// Arguments:      OUT index value if one is available.
//
// Return Values:  E_FAIL if all indeces are in use.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HRESULT    CRilNDIS::NdisGetFreeNdisEntry ( DWORD *lpIndex )
{
    HRESULT hr = E_FAIL;
   
    EnterCriticalSection(&m_csConnectionList); 
    for(DWORD i = FIRST_VALID_NDIS_INDEX; i < MAX_OEMNDIS_CHANNELS; i++)
    {
      if ( 0 == m_rgpNdisChannels[i].hNdisHandle )
      {
         hr = S_OK;
         *lpIndex = i;
         break;
      }
    }
    LeaveCriticalSection(&m_csConnectionList);
    return hr;
}

// **************************************************************************
// Function Name:   NdisRemoveNdisEntry
// 
// Purpose:         Free an NDIS channel for use by others.
//
// Arguments:       IN Handle to the NDIS channel
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void    CRilNDIS::NdisRemoveNdisEntry ( HANDLE hNdisHandle )
{
    EnterCriticalSection(&m_csConnectionList);
    for(int i=FIRST_VALID_NDIS_INDEX;i<MAX_OEMNDIS_CHANNELS;i++)
    {
      if ( hNdisHandle && (m_rgpNdisChannels[i].hNdisHandle == hNdisHandle) ) 
      {
        m_NdisAsyncCommandList.RemoveAllCommands( hNdisHandle );
        CloseHandle(hNdisHandle);
        memset( &m_rgpNdisChannels[i], 0, sizeof(OEMNDISCHANNELS)); 
        break;
      }
    }
    LeaveCriticalSection(&m_csConnectionList);            
}

// **************************************************************************
// Function Name: NdisCidFromHandle
// 
// Purpose:       Find the cid associated to a given NDIS channel handle.
//
// Arguments:     IN NdisHandle, OUT lpCid
//
// Return Values: E_FAIL if not found.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HRESULT  CRilNDIS:: NdisCidFromHandle( HANDLE hNdisHandle, DWORD *lpCid )
{
    HRESULT hr = E_FAIL;
    EnterCriticalSection(&m_csConnectionList); 
    for(int i=FIRST_VALID_NDIS_INDEX; i < MAX_OEMNDIS_CHANNELS; i++)
      if ( hNdisHandle && (m_rgpNdisChannels[i].hNdisHandle == hNdisHandle) )
      {
         *lpCid = m_rgpNdisChannels[i].dwCid;
         hr = S_OK;
         break;
      }

    RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisCidFromHandle hr = %s ContextID = %x \r\n"), 
              (hr == S_OK) ? L"S_OK" : L"E_FAIL", *lpCid));
    LeaveCriticalSection(&m_csConnectionList);    
    return hr;
}

// **************************************************************************
// Function Name: NdisOemNdisOpenRequestFromHandle
// 
// Purpose:       Find the cid associated to a given NDIS channel handle.
//
// Arguments:     IN NdisHandle, OUT lpCid
//
// Return Values: E_FAIL if not found.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HRESULT  CRilNDIS:: NdisOemNdisOpenRequestFromHandle( HANDLE hNdisHandle, RILNDISGPRSCONTEXT *lpRilNdisGprsContext )
{
    HRESULT hr = E_FAIL;
    EnterCriticalSection(&m_csConnectionList);    
    for(int i=FIRST_VALID_NDIS_INDEX; i < MAX_OEMNDIS_CHANNELS; i++)
    {
      if ( hNdisHandle && (m_rgpNdisChannels[i].hNdisHandle == hNdisHandle) && lpRilNdisGprsContext)
      {
         memmove( lpRilNdisGprsContext, &(m_rgpNdisChannels[i].RilNdisGprsContext), sizeof(*lpRilNdisGprsContext));
         hr = S_OK;
         break;
      }
    }

    RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisOemNdisOpenRequestFromHandle hr = %s  \r\n"), 
              (hr == S_OK) ? L"S_OK" : L"E_FAIL"));
    LeaveCriticalSection(&m_csConnectionList);    
    return hr;
}

// **************************************************************************
// Function Name: NdisHandleFromCid
// 
// Purpose:       Search of the NDIS channel handle associated with a cid.
//
// Arguments:     IN cid, OUT hNdisHandle
//
// Return Values: E_FAIL if not found.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HRESULT CRilNDIS::NdisHandleFromCid( DWORD cid, HANDLE *hNdisHandle )
{
    HRESULT hr = E_FAIL;
    EnterCriticalSection(&m_csConnectionList);    
    if ( TRUE == ValidChannelIndex ( cid ) )
    {
        for(int i=FIRST_VALID_NDIS_INDEX;i<MAX_OEMNDIS_CHANNELS;i++)
        {
            if ( (m_rgpNdisChannels[i].dwCid == cid) && m_rgpNdisChannels[i].hNdisHandle )
            {
                *hNdisHandle = m_rgpNdisChannels[i].hNdisHandle;
                hr = S_OK;
                break;
            }
        }
    }
    LeaveCriticalSection(&m_csConnectionList);    
    return hr;
}

// **************************************************************************
// Function Name: NdisRilCallbackFromCID
// 
// Purpose:       Search of the NDIS channel handle associated with a cid.
//
// Arguments:     IN cid, OUT hNdisHandle
//
// Return Values: E_FAIL if not found.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void CRilNDIS::NdisRilCallbacksFromCID( DWORD ContextID, PVOID *pCallbackContext, RILNDISRECEIVECALLBACK *pfnRilReceiveCallback, RILNDISTRANSMITCALLBACK *pfnRilTransmitCallback, RILNDISSTATUSCALLBACK *pfnRilStatusCallback )
{
    EnterCriticalSection(&m_csConnectionList);
    *pfnRilReceiveCallback = NULL;
    *pfnRilTransmitCallback = NULL;
    *pfnRilStatusCallback = NULL;
    *pCallbackContext = NULL;
    for(int i=FIRST_VALID_NDIS_INDEX;i<MAX_OEMNDIS_CHANNELS;i++)
        {
        if ( ContextID && (m_rgpNdisChannels[i].dwCid == ContextID) )
            {
            *pfnRilReceiveCallback = m_rgpNdisChannels[i].RilNdisGprsContext.pfnNdisReceive;
            *pfnRilTransmitCallback = m_rgpNdisChannels[i].RilNdisGprsContext.pfnNdisTransmit;
            *pfnRilStatusCallback = m_rgpNdisChannels[i].RilNdisGprsContext.pfnNdisStatus;
            *pCallbackContext = m_rgpNdisChannels[i].RilNdisGprsContext.pCallbackContext;
            break;
            }
        }
    LeaveCriticalSection(&m_csConnectionList);   
}

// **************************************************************************
// Function Name: NdisRilInstanceFromHandle
// 
// Purpose:       Search of the NDIS channel handle associated with a cid.
//
// Arguments:     IN cid, OUT hNdisHandle
//
// Return Values: E_FAIL if not found.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
VOID* CRilNDIS::NdisRilInstanceFromHandle( HANDLE hNdisHandle )
{
    HRESULT hr = E_FAIL;
    VOID *pVoid = NULL;
    EnterCriticalSection(&m_csConnectionList);
    for(int i=FIRST_VALID_NDIS_INDEX;i<MAX_OEMNDIS_CHANNELS;i++)
    {
        if ( hNdisHandle && (m_rgpNdisChannels[i].hNdisHandle == hNdisHandle) )
        {
            pVoid = m_rgpNdisChannels[i].dwCrilInstance;
            hr = S_OK;
            break;
        }
    }
    LeaveCriticalSection(&m_csConnectionList);   
    return pVoid;
}

// **************************************************************************
// Function Name: NdisRemoveCallbacks
// 
// Purpose:       
//
// Arguments:     
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void    CRilNDIS::NdisRemoveCallbacks ( DWORD ContextID )
{    
    RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisRemoveCallbacks cid = %x  \r\n"), ContextID));
    EnterCriticalSection(&m_csConnectionList);
    for(int i=FIRST_VALID_NDIS_INDEX;i<MAX_OEMNDIS_CHANNELS;i++)
    {
        if ( m_rgpNdisChannels[i].RilNdisGprsContext.dwContextID == ContextID )
        {
            m_rgpNdisChannels[i].RilNdisGprsContext.pCallbackContext = NULL;
            m_rgpNdisChannels[i].RilNdisGprsContext.pfnNdisReceive = NULL;
            m_rgpNdisChannels[i].RilNdisGprsContext.pfnNdisStatus = NULL;
            m_rgpNdisChannels[i].RilNdisGprsContext.pfnNdisTransmit = NULL;
            break;
        }
    }
    LeaveCriticalSection(&m_csConnectionList);
}


// **************************************************************************
// Function Name: NdisAddNdisEntry
// 
// Purpose:       Add an NDIS channel entry to mark its use.
//
// Arguments:     IN cid, dwIndex, hHandle (NDIS channel handle), lpOemNdisEvent CrilInstance open structure
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void    CRilNDIS::NdisAddNdisEntry ( DWORD cid, DWORD dwIndex,  HANDLE hHandle, VOID *hCrilInstance, const RILNDISGPRSCONTEXT *lpRilNdisGprsContext )
{    
    RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisAddNdisEntry cid = %x, index = %x, handle = %x  \r\n"), cid, dwIndex, hHandle));
    EnterCriticalSection(&m_csConnectionList);
    if ( hHandle && (TRUE == ValidChannelIndex ( dwIndex )) )
    {
        m_rgpNdisChannels[dwIndex].dwCid= cid;
        m_rgpNdisChannels[dwIndex].hNdisHandle = hHandle;
        m_rgpNdisChannels[dwIndex].dwCrilInstance = hCrilInstance;
        CeSafeCopyMemory(&m_rgpNdisChannels[dwIndex].RilNdisGprsContext, lpRilNdisGprsContext, sizeof(m_rgpNdisChannels[dwIndex].RilNdisGprsContext));
    }
    LeaveCriticalSection(&m_csConnectionList);
}

// **************************************************************************
// Function Name:  NdisConvertConfig
// 
// Purpose: 
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void CRilNDIS::NdisConvertConfig ( const OEMNDISCONFIGURATION* lpOemNdisConfig,  RILNDISIPCONFIG* lpRilNdisIpConfig)
{
    if ( lpOemNdisConfig && lpRilNdisIpConfig )
    {
        memset( lpRilNdisIpConfig, 0, sizeof(RILNDISIPCONFIG));
        lpRilNdisIpConfig->cbSize = sizeof(RILNDISIPCONFIG);

        /* derive whether address is IPv4(default) or IPv6 */
        lpRilNdisIpConfig->dwProtocol = RIL_PARAM_NDISIPCONFIG_PROTOCOL_IPV4;
        if ( lpOemNdisConfig->ipAddress.length == IPV6_ADDRESS_LENGTH )
        {
            lpRilNdisIpConfig->dwProtocol = RIL_PARAM_NDISIPCONFIG_PROTOCOL_IPV6;
        }

        /* fill in protocol specific parameters */
        if ( lpRilNdisIpConfig->dwProtocol == RIL_PARAM_NDISIPCONFIG_PROTOCOL_IPV4 )
        {
            lpRilNdisIpConfig->ipv4.dwFlags |= RIL_PARAM_NDISIPCONFIG_SUBNETMASK;
            lpRilNdisIpConfig->ipv4.inSubnetMask = 0xffffff00;

            if ( lpOemNdisConfig->ipAddress.length && lpOemNdisConfig->ipAddress.address )
            {
                lpRilNdisIpConfig->ipv4.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPADDR;
                lpRilNdisIpConfig->ipv4.dwFlags |= RIL_PARAM_NDISIPCONFIG_DEFAULTGATEWAY;
                lpRilNdisIpConfig->ipv4.inIPAddress = myinet_addr( lpOemNdisConfig->ipAddress.address, lpOemNdisConfig->ipAddress.length );
                lpRilNdisIpConfig->ipv4.inDefaultGateway = lpRilNdisIpConfig->ipv4.inIPAddress ^ 0xFF;
            }

            if ( lpOemNdisConfig->primaryDnsAddress.length && lpOemNdisConfig->primaryDnsAddress.address)
            {
                lpRilNdisIpConfig->ipv4.dwFlags |= RIL_PARAM_NDISIPCONFIG_PRIMARYDNS;
                lpRilNdisIpConfig->ipv4.inPrimaryDNS = myinet_addr( lpOemNdisConfig->primaryDnsAddress.address, lpOemNdisConfig->primaryDnsAddress.length );
            }

            if ( lpOemNdisConfig->secondaryDnsAddress.length && lpOemNdisConfig->secondaryDnsAddress.address)
            {
                lpRilNdisIpConfig->ipv4.dwFlags |= RIL_PARAM_NDISIPCONFIG_SECONDARYDNS;
                lpRilNdisIpConfig->ipv4.inSecondaryDNS = myinet_addr( lpOemNdisConfig->secondaryDnsAddress.address, lpOemNdisConfig->secondaryDnsAddress.length );
            }
        }
        else if ( lpRilNdisIpConfig->dwProtocol == RIL_PARAM_NDISIPCONFIG_PROTOCOL_IPV6 )
        {
            if ( lpOemNdisConfig->ipAddress.length )
            {
                lpRilNdisIpConfig->ipv6.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPV6_IPADDR;
                lpRilNdisIpConfig->ipv6.inIPAddress = myinet_ipv6addr( lpOemNdisConfig->ipAddress.address, lpOemNdisConfig->ipAddress.length );

                UCHAR subnetmask[IPV6_ADDRESS_LENGTH];
                memset( subnetmask, 0xFF, sizeof(subnetmask) );
                lpRilNdisIpConfig->ipv6.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPV6_SUBNETMASK;
                lpRilNdisIpConfig->ipv6.inSubnetMask = myinet_ipv6addr( subnetmask, IPV6_ADDRESS_LENGTH );

                lpRilNdisIpConfig->ipv6.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPV6_DEFAULTGATEWAY;
                lpRilNdisIpConfig->ipv6.inDefaultGateway = lpRilNdisIpConfig->ipv6.inIPAddress;
            }

            if ( lpOemNdisConfig->primaryDnsAddress.length )
            {
                lpRilNdisIpConfig->ipv6.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPV6_PRIMARYDNS;
                lpRilNdisIpConfig->ipv6.inPrimaryDNS = myinet_ipv6addr( lpOemNdisConfig->primaryDnsAddress.address, lpOemNdisConfig->primaryDnsAddress.length );
            }

            if ( lpOemNdisConfig->secondaryDnsAddress.length )
            {
                lpRilNdisIpConfig->ipv6.dwFlags |= RIL_PARAM_NDISIPCONFIG_IPV6_SECONDARYDNS;
                lpRilNdisIpConfig->ipv6.inSecondaryDNS = myinet_ipv6addr( lpOemNdisConfig->secondaryDnsAddress.address, lpOemNdisConfig->secondaryDnsAddress.length );
            }
        }
    }
}

// **************************************************************************
// Function Name:  NdisCloseAllConnections
// 
// Purpose:        Close all NDIS connections
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void   CRilNDIS::NdisCloseAllConnections ( void )
{
    EnterCriticalSection(&m_csConnectionList);
    DWORD ContextID;
    RILGPRSCONTEXTACTIVATED RilGprsContexActivated;
    RilGprsContexActivated.cbSize = sizeof(RILGPRSCONTEXTACTIVATED);
    RilGprsContexActivated.fActivated = FALSE;
    RilGprsContexActivated.dwEvent = RIL_RILGPRSCONTEXTACTIVATED_MEDEACT;


    for(int i=FIRST_VALID_NDIS_INDEX;i<MAX_OEMNDIS_CHANNELS;i++)
        {
        if ( m_rgpNdisChannels[i].hNdisHandle  )
            {
            if ( S_OK  == NdisCidFromHandle( m_rgpNdisChannels[i].hNdisHandle, &ContextID) )
                {
                RemoveAndSignalAllPdpContextEntries(ContextID);
                }
            if ( S_OK  == NdisCidFromHandle( m_rgpNdisChannels[i].hNdisHandle, &RilGprsContexActivated.dwContextID) )
            ((CRilHandle*)m_pCrilHandle)->BroadcastRealBlobNotification(RIL_NOTIFY_GPRSCONNECTIONSTATUS, &RilGprsContexActivated, sizeof(RILGPRSCONTEXTACTIVATED));

            // Remove the entry from our list of Ndis handles/Cids
            NdisRemoveNdisEntry(m_rgpNdisChannels[i].hNdisHandle);
            // Now remove the event object.
            NdisRemoveNotifyEvent(m_rgpNdisChannels[i].hNdisHandle);
            }
        }
    LeaveCriticalSection(&m_csConnectionList);
}

// **************************************************************************
// Function Name:   NdisShutdownConnection
// 
// Purpose:         Shutdown and NDIS connection and cleanup related items.
//
// Arguments:       IN handle to channel to close.
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void CRilNDIS:: NdisShutdownConnection ( HANDLE hHandle )
{

    if ( hHandle )
    {
        // We want to Shutdown this handle and remove from NDIS channel information.
        NdisInterfaceShutdown(hHandle);         
        // Notify it's down
        RILGPRSCONTEXTACTIVATED RilGprsContexActivated;
        RilGprsContexActivated.cbSize = sizeof(RILGPRSCONTEXTACTIVATED);
        RilGprsContexActivated.fActivated = FALSE;
        RilGprsContexActivated.dwEvent = RIL_RILGPRSCONTEXTACTIVATED_MEDEACT;
        if ( S_OK  == NdisCidFromHandle( hHandle, &RilGprsContexActivated.dwContextID) )
            ((CRilHandle*)m_pCrilHandle)->BroadcastRealBlobNotification(RIL_NOTIFY_GPRSCONNECTIONSTATUS, &RilGprsContexActivated, sizeof(RILGPRSCONTEXTACTIVATED));
        // Remove the entry from our list of Ndis handles/Cids
        NdisRemoveNdisEntry(hHandle);
        // Now remove the event object.
        NdisRemoveNotifyEvent(hHandle);
    }
}

void RilNdisSendPacket (const LPRILNDISPACKET lpPacketToSend)
{
    if ( g_pCRilNdis && lpPacketToSend)
    {
       {
           HANDLE hNdis;
           if ( S_OK == g_pCRilNdis->NdisHandleFromCid( lpPacketToSend->dwContextId, &hNdis ))
           {
               CRilInstanceNDIS *pCRilInstanceNdis;
               pCRilInstanceNdis = (CRilInstanceNDIS*)g_pCRilNdis->NdisRilInstanceFromHandle( hNdis );
               if ( pCRilInstanceNdis )
               {
               pCRilInstanceNdis->NdisSendPacket(lpPacketToSend);
               }
           }
       }          
    }
}

void RilNdisRecievePacketDone(const LPRILNDISPACKET lpPacketReceived)
{
    if ( g_pCRilNdis && lpPacketReceived)
    {
       {
           HANDLE hNdis;
           if ( S_OK == g_pCRilNdis->NdisHandleFromCid( lpPacketReceived->dwContextId, &hNdis ))
           {
               CRilInstanceNDIS *pCRilInstanceNdis;
               pCRilInstanceNdis = (CRilInstanceNDIS*)g_pCRilNdis->NdisRilInstanceFromHandle( hNdis );
               if ( pCRilInstanceNdis )
               {
                   pCRilInstanceNdis->NdisReceivePacketDone(lpPacketReceived);
               }
           }
       }          
    }    
}
// **************************************************************************
// Function Name: NdisNotifyThread
// 
// Purpose:       Thread running to process NDIS events.
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
DWORD   CRilNDIS:: NdisNotifyThread(void)
{
    DWORD dwTriggered, dwIndex, ChannelIndex=0;
    BOOL fMoreEvents,fSuccess;
    OEMNDISAPIREFERENCE OemNdisApiReference;
    HRESULT hr;
    RILNDISIPCONFIG RilNdisIpConfig;
    OEMNDISEVENT OemNdisEvent,ReceiveOemNdisEvent;
    CRilInstanceHandle *pCrilInstanceHandle = NULL;
    RILNDISGPRSCONTEXT RilNdisGprsContext;
    RILGPRSCONTEXTACTIVATED RilGprsContexActivated;
    DWORD               ResultDetails = 0;
    DWORD               ContextID = 0;
    HANDLE              hNdisChannelHandle = NULL;
    BOOL                HandlePdpContextListEntry = FALSE;
    BOOL                ProcessCommandFlag = FALSE;
    RILNDISTRANSMITCALLBACK pfnRilTransmitCallback;
    RILNDISSTATUSCALLBACK   pfnRilStatusCallback;
    RILNDISRECEIVECALLBACK pfnRilReceiveCallback;
    PVOID                  pCallbackContext;
    RILNDISSTATUS       RilNdisStatus;
    DWORD               ContextIDIndex;

    // Tell the main thread that we've reached the checkpoint
    ((CRilHandle*)m_pCrilHandle)->GetCheckPoint()->Reached();
    
    memset ( &m_rgpNdisEvents, 0, sizeof(OEMNDISWAITEVENTOBJECT));

    // Create the event used to indicate new events
    m_rgpNdisEvents.hNdisAddChannelEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if(!m_rgpNdisEvents.hNdisAddChannelEvent)
    {
        goto Exit;
    }

    m_rgpNdisEvents.lpNdisEventHandlesPrimary[NDIS_ADD_CHANNEL_EVENT_INDEX] = m_rgpNdisEvents.hNdisAddChannelEvent ;
    m_rgpNdisEvents.lpNdisEventHandlesUpdate[NDIS_ADD_CHANNEL_EVENT_INDEX] = m_rgpNdisEvents.hNdisAddChannelEvent ;
    m_rgpNdisEvents.nObjectCount = 1;

    m_rgpNdisEvents.lpNdisEventHandlesPrimary[NDIS_STOP_THREAD_INDEX] = m_hCancelEvent;
    m_rgpNdisEvents.lpNdisEventHandlesUpdate[NDIS_STOP_THREAD_INDEX] =  m_hCancelEvent;
    m_rgpNdisEvents.nObjectCount++;
    
    do 
    {
        dwTriggered = WaitForMultipleObjects( m_rgpNdisEvents.nObjectCount, m_rgpNdisEvents.lpNdisEventHandlesPrimary, FALSE, INFINITE );
        dwIndex = dwTriggered - WAIT_OBJECT_0;

        // The events from NDIS are created with manual reset enabled. The event will be reset by the RADIO when
        // the last event is read from the queue.
        if ( dwIndex == NDIS_ADD_CHANNEL_EVENT_INDEX ) // index reserved for the m_rgpNdisEvents.hNdisAddChannelEvent  event.
        {
            EnterCriticalSection(&m_csNdisEventList);
            memmove( m_rgpNdisEvents.lpNdisEventHandlesPrimary, 
                     m_rgpNdisEvents.lpNdisEventHandlesUpdate, 
                     sizeof(m_rgpNdisEvents.lpNdisEventHandlesPrimary));
        
            m_rgpNdisEvents.nObjectCount = NdisCountEvents();
            LeaveCriticalSection(&m_csNdisEventList);
            continue; // Get the upcoming notification
        }
        
        if ( dwIndex == NDIS_STOP_THREAD_INDEX )
        {
            NdisCloseAllConnections();
            break;
        }

        // The notification is coming from the RADIO
        else 
        {
            // We maintain a parallel array that contains the RADIO handle so we don't need to extract them from the event 
            // name.  Get RADIO events and process them.
            do {
                   memset ( &ReceiveOemNdisEvent, 0, sizeof(OEMNDISEVENT));
                   fSuccess = NdisInterfaceReceiveEvent( m_rgpNdisEvents.lpNdisHandles[dwIndex],  &ReceiveOemNdisEvent, &fMoreEvents);
                   if ( fSuccess )
                   {
                       DEBUG_TYPE(ReceiveOemNdisEvent.type);
                       memset(&OemNdisApiReference,0,sizeof(OemNdisApiReference)); 
                       OemNdisApiReference.hNdisHandle = m_rgpNdisEvents.lpNdisHandles[dwIndex]; // the radio handle
               
                       switch ( ReceiveOemNdisEvent.type )
                       {
                           case OEM_NDIS_RX_PACKET: 
                               ReceiveOemNdisEvent.event.packet_p->dwContextId = 0; 
                               NdisCidFromHandle ( m_rgpNdisEvents.lpNdisHandles[dwIndex], &ReceiveOemNdisEvent.event.packet_p->dwContextId );    
        
                               DumpPacket(ReceiveOemNdisEvent.event.packet_p);
                               PacketPointers ( ReceiveOemNdisEvent.event.packet_p );

                               pfnRilReceiveCallback = NULL;
                               NdisRilCallbacksFromCID( ReceiveOemNdisEvent.event.packet_p->dwContextId,  &pCallbackContext, &pfnRilReceiveCallback, &pfnRilTransmitCallback, &pfnRilStatusCallback );

                               if ( pfnRilReceiveCallback )
                               {
                                   pfnRilReceiveCallback(pCallbackContext,ReceiveOemNdisEvent.event.packet_p);
                               }
                               else // The context must be closed
                               {
                                   RETAILMSG(MSG_ON, (TEXT("RILNDIS: OEM_NDIS_RX_PACKET WARNING Context Closed - Freeing \r\n")));  
                                   ReceiveOemNdisEvent.type = OEM_NDIS_RX_PACKET_COMPLETE;
                                   NdisInterfaceTransmitEvent( m_rgpNdisEvents.lpNdisHandles[dwIndex], &ReceiveOemNdisEvent);
                               }        
                               break;
                    
                           case OEM_NDIS_TX_PACKET_COMPLETE:
                               NdisCidFromHandle ( m_rgpNdisEvents.lpNdisHandles[dwIndex], &ReceiveOemNdisEvent.event.packet_p->dwContextId );            
                               pfnRilTransmitCallback = NULL;
                               NdisRilCallbacksFromCID( ReceiveOemNdisEvent.event.packet_p->dwContextId,  &pCallbackContext, &pfnRilReceiveCallback, &pfnRilTransmitCallback, &pfnRilStatusCallback );

                               if ( pfnRilTransmitCallback )
                               {
                                   pfnRilTransmitCallback(pCallbackContext,ReceiveOemNdisEvent.event.packet_p);
                               }
                               else // The context must be closed
                               {
                                   RETAILMSG(MSG_ON, (TEXT("RILNDIS: OEM_NDIS_TX_PACKET_COMPLETE WARNING Context Closed - Freeing \r\n")));  
                               }        
                               break;
                    
                           case OEM_NDIS_IP_CONFIGURATION:     
                               memset ( &RilNdisIpConfig, 0, sizeof(RILNDISIPCONFIG));
                               NdisConvertConfig ( &ReceiveOemNdisEvent.event.configuration, &RilNdisIpConfig);
                               NdisCidFromHandle ( m_rgpNdisEvents.lpNdisHandles[dwIndex], &RilNdisIpConfig.dwContextId ); 
                               DumpNdisIPConfig( &RilNdisIpConfig );
                               OemNdisApiReference.RilNdisGprsContextResponse.cbSize = sizeof(OemNdisApiReference.RilNdisGprsContextResponse);
                               OemNdisApiReference.dwStatus = 0; // No error
                               OemNdisApiReference.eEventType= OEM_NDIS_OPEN_COMPLETE;
                               OemNdisApiReference.ContextID = ContextID;
                               OemNdisApiReference.RilNdisGprsContextResponse.dwError = OemNdisApiReference.dwStatus;
                               ContextID = 0;
                               NdisCidFromHandle( m_rgpNdisEvents.lpNdisHandles[dwIndex], &ContextID);
                               OemNdisApiReference.RilNdisGprsContextResponse.dwContextID = ContextID;
                               OemNdisApiReference.RilNdisGprsContextResponse.pfnNdisReceivePacketDone = RilNdisRecievePacketDone;
                               OemNdisApiReference.RilNdisGprsContextResponse.pfnNdisSendPacket = RilNdisSendPacket;
                		       CeSafeCopyMemory(&OemNdisApiReference.RilNdisGprsContextResponse.RilNdisIPConfig, &RilNdisIpConfig,sizeof(OemNdisApiReference.RilNdisGprsContextResponse.RilNdisIPConfig));
                               OemNdisApiReference.RilNdisGprsContextResponse.dwParams = RIL_PARAM_RILNDISGPRSCONTEXTRESPONSE_IPCONFIG | RIL_PARAM_RILNDISGPRSCONTEXTRESPONSE_FUNCTIONS;
                               ProcessCommandFlag = m_NdisAsyncCommandList.ProcessCommand ( &OemNdisApiReference ); 
                               ProcessNextContextRequest();                       
                               break;
                    
                           case OEM_NDIS_XOFF:   
                           case OEM_NDIS_XON:   
                                RilNdisStatus.dwContextId=0;
                                RilNdisStatus.dwSize=sizeof(RilNdisStatus);
                                RilNdisStatus.dwStatusType=RIL_PARAM_RILNDISSTATUS_FLOWCONTROL;
                                RilNdisStatus.dwFlowControl = (ReceiveOemNdisEvent.type == OEM_NDIS_XON) ? RIL_NDIS_XON : RIL_NDIS_XOFF;
                                m_fXon = RilNdisStatus.dwFlowControl;
                                pfnRilStatusCallback = NULL;
                                for( ContextIDIndex=FIRST_VALID_NDIS_INDEX;ContextIDIndex<MAX_OEMNDIS_CHANNELS;ContextIDIndex++)
                                    {   
                                    NdisRilCallbacksFromCID( ContextIDIndex,  &pCallbackContext, &pfnRilReceiveCallback, &pfnRilTransmitCallback, &pfnRilStatusCallback );
   
                                    if ( pfnRilStatusCallback )
                                        {
                                        pfnRilStatusCallback(pCallbackContext,&RilNdisStatus);
                                        }
                                    else 
                                        {
                                        RETAILMSG(MSG_ON, (TEXT("RILNDIS: %s WARNING No Callback on context %x\r\n"),(ReceiveOemNdisEvent.type == OEM_NDIS_XON) ? L"OEM_NDIS_XON" : L"OEM_NDIS_XOFF", ContextIDIndex));  
                                        }   
                                    }                      
                               break;
                    
                           case OEM_NDIS_INITIALISE_COMPLETE:
                                ContextID = 0;
                                NdisCidFromHandle( m_rgpNdisEvents.lpNdisHandles[dwIndex], &ContextID);
                                ASSERT(ContextID);
                                
                                if ( ReceiveOemNdisEvent.event.NdisStatus.dwErrorStatus == 0 ) // SUCCESS
                                    {                               
                                    memset(&OemNdisEvent, 0, sizeof ( OEMNDISEVENT ));
                                    OemNdisEvent.type = OEM_NDIS_OPEN_CONNECTION;
                                    hr = NdisOemNdisOpenRequestFromHandle(m_rgpNdisEvents.lpNdisHandles[dwIndex], &RilNdisGprsContext);
                                    if ( hr == S_OK )
                                        {
                                        if ( RilNdisGprsContext.dwParams & RIL_PARAM_RILNDISGPRSCONTEXT_USERNAME )
                                            {
                                            strncpy(OemNdisEvent.event.open.psUsername,AnsiString(RilNdisGprsContext.tszUserName),NDIS_USERNAME_MAX_LENGTH-1);
                                            strncpy(OemNdisEvent.event.open.psPassword,AnsiString(RilNdisGprsContext.tszPassword),NDIS_PASSWORD_MAX_LENGTH-1);
                                            OemNdisEvent.event.open.dwUsernameLength = strlen(OemNdisEvent.event.open.psUsername);
                                            OemNdisEvent.event.open.dwPasswordLength = strlen(OemNdisEvent.event.open.psPassword);
                                            RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisNotifyThread  Username = %hs,  Length = %d,  Password = %hs  Length = %d  \r\n"), 
                                                    OemNdisEvent.event.open.psUsername, OemNdisEvent.event.open.dwUsernameLength,
                                                    OemNdisEvent.event.open.psPassword,OemNdisEvent.event.open.dwPasswordLength ));
                                            }
                                        OemNdisEvent.event.open.cid = ContextID;
                                        }
                                    if ( !NdisInterfaceTransmitEvent( m_rgpNdisEvents.lpNdisHandles[dwIndex], &OemNdisEvent) )
                                        {
                                        hr = E_FAIL;
                                        ReceiveOemNdisEvent.event.NdisStatus.dwErrorStatus = 1; // Set error
                                        }
                                   }
                                else
                                    {
                                    hr = E_FAIL;
                                    }
                               
                                if ( S_OK != hr )
                                    {
                                    OemNdisApiReference.eEventType= OEM_NDIS_OPEN_COMPLETE;
                                    OemNdisApiReference.dwStatus = ReceiveOemNdisEvent.event.NdisStatus.dwErrorStatus;
                                    OemNdisApiReference.ContextID = ContextID;
                                    OemNdisApiReference.RilNdisGprsContextResponse.cbSize = sizeof(OemNdisApiReference.RilNdisGprsContextResponse);
                                    OemNdisApiReference.RilNdisGprsContextResponse.dwError = OemNdisApiReference.dwStatus;
                                    ProcessCommandFlag = m_NdisAsyncCommandList.ProcessCommand ( &OemNdisApiReference );
                                    ASSERT(ProcessCommandFlag);
                                    NdisShutdownConnection ( m_rgpNdisEvents.lpNdisHandles[dwIndex] );
                                    ProcessNextContextRequest();
                                    }
                               break;
                    
                           case OEM_NDIS_OPEN_COMPLETE:     
                               ContextID = 0;
                               NdisCidFromHandle( m_rgpNdisEvents.lpNdisHandles[dwIndex], &ContextID);
                               ASSERT(ContextID);
                               NdisRilCallbacksFromCID( ContextID,  &pCallbackContext, &pfnRilReceiveCallback, &pfnRilTransmitCallback, &pfnRilStatusCallback );                                       
                               if ( NULL == pfnRilStatusCallback )
                                   {
                                   ReceiveOemNdisEvent.event.NdisStatus.dwErrorStatus = RIL_E_CANCELLED;
                                   }
                               OemNdisApiReference.dwStatus = ReceiveOemNdisEvent.event.NdisStatus.dwErrorStatus;
                               OemNdisApiReference.eEventType= OEM_NDIS_OPEN_COMPLETE;
                               OemNdisApiReference.ContextID = ContextID;
                               OemNdisApiReference.RilNdisGprsContextResponse.dwError = OemNdisApiReference.dwStatus;
                               OemNdisApiReference.RilNdisGprsContextResponse.cbSize = sizeof(OemNdisApiReference.RilNdisGprsContextResponse);
                               if ( ReceiveOemNdisEvent.event.NdisStatus.dwErrorStatus != 0 ) // FAILURE!
                               {
                                   ProcessCommandFlag = m_NdisAsyncCommandList.ProcessCommand ( &OemNdisApiReference );  
                                   NdisShutdownConnection ( m_rgpNdisEvents.lpNdisHandles[dwIndex] );
                                   ProcessNextContextRequest();
                                   ASSERT(ProcessCommandFlag);                
                               }
                               else
                               {
                                   RilGprsContexActivated.cbSize = sizeof(RILGPRSCONTEXTACTIVATED);
                                   RilGprsContexActivated.fActivated = TRUE;
                                   RilGprsContexActivated.dwContextID = ContextID;
                                   RilGprsContexActivated.dwEvent = RIL_RILGPRSCONTEXTACTIVATED_MEACT;
                                   ((CRilHandle*)m_pCrilHandle)->BroadcastRealBlobNotification(RIL_NOTIFY_GPRSCONNECTIONSTATUS, &RilGprsContexActivated, sizeof(RILGPRSCONTEXTACTIVATED));
                               }
                               break;
                    
                           case OEM_NDIS_CLOSE_COMPLETE:  
                               ContextID = 0;
                               NdisCidFromHandle( m_rgpNdisEvents.lpNdisHandles[dwIndex], &ContextID);
                               ASSERT(ContextID);
                               OemNdisApiReference.dwStatus = ReceiveOemNdisEvent.event.NdisStatus.dwErrorStatus;
                               OemNdisApiReference.eEventType= OEM_NDIS_CLOSE_COMPLETE;
                               OemNdisApiReference.ContextID = ContextID;
                               ProcessCommandFlag=m_NdisAsyncCommandList.ProcessCommand ( &OemNdisApiReference );
                               NdisShutdownConnection ( m_rgpNdisEvents.lpNdisHandles[dwIndex] ); 
                               ProcessNextContextRequest();
                               break;
                               
                           case OEM_NDIS_REGISTRATION_STATUS:
                               RETAILMSG(MSG_ON, (TEXT("RILNDIS:  fGsmRegistered = %x, fGprsAttached = %x\r\n"), 
                                                         ReceiveOemNdisEvent.event.regStatus.fGsmRegistered, 
                                                         ReceiveOemNdisEvent.event.regStatus.fGprsAttached));
                               if ( FALSE == ReceiveOemNdisEvent.event.regStatus.fGprsAttached )
                               {
                                   if ( S_OK  == NdisCidFromHandle( m_rgpNdisEvents.lpNdisHandles[dwIndex], &ContextID) )
                                   {
                                       RilGprsContexActivated.cbSize = sizeof(RILGPRSCONTEXTACTIVATED);
                                       RilGprsContexActivated.fActivated = FALSE;
                                       RilGprsContexActivated.dwContextID = ContextID;
                                       RilGprsContexActivated.dwEvent = RIL_RILGPRSCONTEXTACTIVATED_NWDEACT;
                                       ((CRilHandle*)m_pCrilHandle)->BroadcastRealBlobNotification(RIL_NOTIFY_GPRSCONNECTIONSTATUS, &RilGprsContexActivated, sizeof(RILGPRSCONTEXTACTIVATED));
                                   }
                               }
                               break;
                               
                           case OEM_NDIS_BUFFER_STATUS:
                               RETAILMSG(MSG_ON, (TEXT("RILNDIS:  reason = %x, maxRxBuffers = %x rxBuffersInUse = %0x  maxTxBuffers = %0x txBuffersQueued = %0x \r\n"), 
                                                     ReceiveOemNdisEvent.event.bufferStatus.eReason, 
                                                     ReceiveOemNdisEvent.event.bufferStatus.dwMaxRxBuffers,
                                                     ReceiveOemNdisEvent.event.bufferStatus.dwRxBuffersInUse,
                                                     ReceiveOemNdisEvent.event.bufferStatus.dwMaxTxBuffers,
                                                     ReceiveOemNdisEvent.event.bufferStatus.dwTxBuffersQueued));
                               break;
                
                           case OEM_NDIS_NULL_EVENT: 
                               break;
                    
                       }
                   }
               }while ( fMoreEvents && fSuccess );
        }
    }while(1);

Exit:
    CloseHandle ( m_rgpNdisEvents.hNdisAddChannelEvent );
    
    return 0;
}

// **************************************************************************
// Function Name: NdisAddNotifyEvent
// 
// Purpose:       Locate an event created when an NDIS channel is opened. Add the
//                event to the update event list and signal the NDIS thread that
//                there are more events to wait for.
//
// Arguments:     IN Handle of the newly created NDIS channel.
//
// Return Values: S_OK if event was found.
//
// Side effects:  
// 
// Description:   
// **************************************************************************

HRESULT CRilNDIS:: NdisAddNotifyEvent(HANDLE hNdisHandle)
{
    TCHAR *pszEventName = NULL;
    HANDLE hHandle;
    HRESULT hr = E_FAIL;

    if ( hNdisHandle )
    {   
        pszEventName = new TCHAR[MAX_NDIS_EVENT_NAME];
    
        if ( pszEventName )
        {
            hr = StringCchPrintf(pszEventName, MAX_NDIS_EVENT_NAME, TEXT("%s%08x"),
                                 TEXT(NDIS_EVENT_PREFIX), hNdisHandle);
        
            if ( SUCCEEDED(hr) )
            {
                hr = E_FAIL; // Reset 
                // Find the event associated with this handle suffix
                hHandle = CreateEvent( NULL, FALSE, FALSE, pszEventName);
                if ( hHandle )
                {  
                    // The event should already exist. If it doesn't, something is wrong.
                    if ( ERROR_ALREADY_EXISTS == GetLastError ())
                    {                
                        EnterCriticalSection(&m_csNdisEventList);
                        // Now we can add the event to the structure and notify the event thread
                        for (int i=0;i<MAX_OEMNDIS_EVENTS;i++)
                        {
                          if (!m_rgpNdisEvents.lpNdisEventHandlesUpdate[i] )
                          {
                            hr = S_OK;
                            m_rgpNdisEvents.lpNdisEventHandlesUpdate[i] = hHandle; // store the handles used for WaitForMultipleObjects
                            m_rgpNdisEvents.lpNdisHandles[i] = hNdisHandle; // store the quick (NDIS) handle parallel to it.
                            SetEvent(m_rgpNdisEvents.hNdisAddChannelEvent );
                            break;
                          }
                        }
                        LeaveCriticalSection(&m_csNdisEventList);
                    }
                    if ( S_OK != hr )
                    {
                        CloseHandle(hHandle);
                    }
                }
            }
            delete [] pszEventName;
        }
    }
    return hr;    
}


// **************************************************************************
// Function Name:   NdisRemoveNotifyEvent
// 
// Purpose:    Locate and remove the event associated with an NDIS channel. This
//             function is called when when the NDIS channel is closed. Remove
//             the event from the update list and signal the NDIS thread that 
//             the list is updated. After the event is removed from the list,
//             shift the list to eliminate any holes.
//                                 
//
// Arguments:  handle associated with the NDIS channel that has been closed.
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************  
HRESULT CRilNDIS:: NdisRemoveNotifyEvent(HANDLE hNdisHandle)
{
    HRESULT hr = S_OK;
    BOOL fFound = FALSE;
    
    if ( !hNdisHandle ) 
    {
      hr = E_FAIL;
      goto Error;
    }

    EnterCriticalSection(&m_csNdisEventList);
    
    for (int i=0;i<MAX_OEMNDIS_EVENTS;i++)
    {
        // We search the NDIS handle list, not the event handle list.
        if ( m_rgpNdisEvents.lpNdisHandles[i] == hNdisHandle ) 
        {
            // We don't worry about destroying the object because NDIS radio support does it for us.
            m_rgpNdisEvents.lpNdisEventHandlesUpdate[i] = 0;  // the event handle.
            m_rgpNdisEvents.lpNdisHandles[i] = 0;          // the ndis handle.
            m_rgpNdisEvents.nObjectCount--; // One more thing to wait for.
            fFound = TRUE;
            break;
        }
    }
    
    // Now we have a list of object handles that may have a hole in it. Make a new list without holes.
    // Now we can add the event to the structure and notify the event thread
    for (int i=0;i<MAX_OEMNDIS_EVENTS-1;i++)
    {
        if ( !m_rgpNdisEvents.lpNdisEventHandlesUpdate[i]  )
        {
            if ( m_rgpNdisEvents.lpNdisEventHandlesUpdate[i+1] )
            {
                m_rgpNdisEvents.lpNdisEventHandlesUpdate[i] = m_rgpNdisEvents.lpNdisEventHandlesUpdate[i+1] ;
                m_rgpNdisEvents.lpNdisHandles[i] = m_rgpNdisEvents.lpNdisHandles[i+1];
                m_rgpNdisEvents.lpNdisHandles[i+1] = 0;
                m_rgpNdisEvents.lpNdisEventHandlesUpdate[i+1] = 0;
            }
        }
    }
    
    // Notify the NDIS thread to update the waiting list.
    if ( fFound )
        SetEvent(m_rgpNdisEvents.hNdisAddChannelEvent );
    
    LeaveCriticalSection(&m_csNdisEventList);

Error:
    return hr;
}

// **************************************************************************
// Function Name: NdisInterfaceInitialise
// 
// Purpose:       Emulate a single NDIS Channel
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HANDLE  CRilNDIS::NdisInterfaceInitialise (DWORD index)
{
    if ( index >= FIRST_VALID_NDIS_INDEX )
    {
        RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisInterfaceInitialise index = %x\r\n"), index));
        return(g_pfnNdisInitialize ( index));
    }
    else
        return NULL;
};

// **************************************************************************
// Function Name:  NdisInterfaceShutdown
// 
// Purpose:        Emulate a single NDIS Channel
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
BOOL CRilNDIS::NdisInterfaceShutdown (HANDLE handle)
{
    if ( handle )
    {
        RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisInterfaceShutdown handle = %x\r\n"),handle));
        return(g_pfnNdisShutdown(handle));
    }
    else
        return FALSE;
};

// **************************************************************************
// Function Name: NdisInterfaceReceiveEvent
// 
// Purpose:       Emulate a single NDIS Channel
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
BOOL CRilNDIS::NdisInterfaceReceiveEvent(HANDLE handle, OEMNDISEVENT *event_p, BOOL *moreEvents_p)
{
    if ( handle && event_p && moreEvents_p )    
    {
        RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisInterfaceReceiveEvent handle = %x\r\n"),handle));
        return(g_pfnNdisReceiveEvent(handle,event_p,moreEvents_p));
    }
    else
        return FALSE;
};

// **************************************************************************
// Function Name:   NdisInterfaceTransmitEvent
// 
// Purpose:         Simulate the operation of and NDIS radio.
//
// Arguments:       IN handle - unique to NDIS channel opened.
//                  IN event_p - defintion of the event being sent.
//
// Return Values:   TRUE if successful.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
 BOOL CRilNDIS::NdisInterfaceTransmitEvent(HANDLE handle, const OEMNDISEVENT *event_p)
{
    BOOL fReturn = FALSE;
    
    if ( handle && event_p )
    {
        fReturn = g_pfnNdisTransmitEvent(handle,event_p);
    
        RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisInterfaceTransmitEvent handle = %x Return = %x\r\n"),handle, fReturn));
    }
    
    return fReturn;
};
 
// **************************************************************************
// Function Name:   NdisAllocateCommand
// 
// Purpose:    Allocate an asynchronous command. This method is used
//             when the response will key off of the event type.
//
// Arguments:  IN eType: expected event.
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
void CRilNDIS::NdisAllocateCommand (  OEMNDISEVENTTYPE eType, HANDLE hNdisChannelHandle, LPVOID lpCrilInstance, HRESULT CommandID, DWORD ContextID )
{
    if ( hNdisChannelHandle && lpCrilInstance )
    {
        OEMNDISAPIREFERENCE OemNdisApiReference;
        memset(&OemNdisApiReference,0,sizeof(OemNdisApiReference));
    
        OemNdisApiReference.dwCrilInstance = lpCrilInstance;
        OemNdisApiReference.eEventType = eType;
        OemNdisApiReference.hNdisHandle = hNdisChannelHandle;
        OemNdisApiReference.hrCommandID = CommandID; 
        OemNdisApiReference.ContextID = ContextID;
        m_NdisAsyncCommandList.AddCommand ( &OemNdisApiReference );
    }
}

void
CRilNDIS:: NdisRemoveCommand ( HRESULT hrCommandID )
{
    if ( hrCommandID )
        {
        m_NdisAsyncCommandList.RemoveCommand ( hrCommandID );
        }
}

inline void
CRilNDIS::PdpContextLock()
{
    EnterCriticalSection(&m_PdpContextCriticalSection);
}

inline void
CRilNDIS::PdpContextUnlock()
{
    LeaveCriticalSection(&m_PdpContextCriticalSection);
}

inline BOOL
CRilNDIS::IsPdpContextLocked()
{
    return m_PdpContextCriticalSection.OwnerThread == (HANDLE)GetCurrentThreadId();
}

PPDP_CONTEXT_ENTRY
CRilNDIS::PdpContextEntryGet()
{
    PPDP_CONTEXT_ENTRY pEntry = NULL;

    ASSERT(IsPdpContextLocked());

    if (!IsListEmpty(&m_PdpContextFreeList))
        {
        pEntry = (PPDP_CONTEXT_ENTRY)RemoveHeadList(&m_PdpContextFreeList);
        }

    if (pEntry == NULL)
        {
        pEntry = (PPDP_CONTEXT_ENTRY)LocalAlloc(LPTR, sizeof(*pEntry));
        }

    if ( pEntry )
        {
        pEntry->AsyncCommandID = 0;
        pEntry->pCRilInstance = NULL;
        memset(&pEntry->RilNdisGprsContext, 0, sizeof(pEntry->RilNdisGprsContext));
        }

    return pEntry;
}

void 
CRilNDIS::DumpPdpContextList ( void )
{

    PPDP_CONTEXT_ENTRY pEntry;
    PdpContextLock();
    //
    // Find entry with matching ContextID in our list of pending requests
    //

    for (pEntry = (PPDP_CONTEXT_ENTRY)(m_PdpContextPendingList.Flink);
         TRUE;
         pEntry = (PPDP_CONTEXT_ENTRY)pEntry->Node.Flink)
        {
        if (pEntry == (PPDP_CONTEXT_ENTRY)(&m_PdpContextPendingList))
            {
            break;
            }
        RETAILMSG(MSG_ON, (TEXT("RILNDIS: DumpPdpContextList  pPdpContextEntry = %x \r\n"), pEntry));
        }
    PdpContextUnlock();

}

void 
CRilNDIS::AddPdpContextEntry( const RILNDISGPRSCONTEXT *pRilNdisGprsContext, HRESULT AsyncCommandID, PVOID pCRilInstance )
{
    PPDP_CONTEXT_ENTRY pEntry;
    PdpContextLock();
    pEntry = PdpContextEntryGet();
    ASSERT(pEntry);
    if ( pEntry )
        {
        pEntry->AsyncCommandID = AsyncCommandID;
        pEntry->pCRilInstance = pCRilInstance;
        CeSafeCopyMemory(&pEntry->RilNdisGprsContext,pRilNdisGprsContext,sizeof(pEntry->RilNdisGprsContext));
        InsertTailList(&m_PdpContextPendingList, &pEntry->Node);
        }
    PdpContextUnlock();
    RETAILMSG(MSG_ON, (TEXT("RILNDIS: AddPdpContextEntry  pPdpContextEntry = %x \r\n"), pEntry));
    DumpPdpContextList();
}

void 
CRilNDIS::RemovePdpContextEntry( PPDP_CONTEXT_ENTRY pPdpContextEntry )
{
    PPDP_CONTEXT_ENTRY pEntry;
    PdpContextLock();
    //
    // Find entry with matching ContextID in our list of pending requests
    //

    RETAILMSG(MSG_ON, (TEXT("RILNDIS: RemovePdpContextEntry  pPdpContextEntry = %x \r\n"), pPdpContextEntry));

    for (pEntry = (PPDP_CONTEXT_ENTRY)(m_PdpContextPendingList.Flink);
         TRUE;
         pEntry = (PPDP_CONTEXT_ENTRY)pEntry->Node.Flink)
        {
        if (pEntry == (PPDP_CONTEXT_ENTRY)(&m_PdpContextPendingList))
            {
            ASSERT(0);
            break;
            }

        if ( pEntry == pPdpContextEntry )
            {
            RemoveEntryList(&pEntry->Node);
            InsertTailList(&m_PdpContextFreeList, &pEntry->Node);
            break;
            }
        }
    PdpContextUnlock();
    DumpPdpContextList();

}


PPDP_CONTEXT_ENTRY
CRilNDIS::GetNextPdpContextEntry(  void )
{
    PPDP_CONTEXT_ENTRY pEntry;
    PPDP_CONTEXT_ENTRY pReturnEntry = NULL;
    PdpContextLock();
    //
    // Find entry with matching ContextID in our list of pending requests
    //
    for (pEntry = (PPDP_CONTEXT_ENTRY)(m_PdpContextPendingList.Flink);
         TRUE;
         pEntry = (PPDP_CONTEXT_ENTRY)pEntry->Node.Flink)
        {
        if (pEntry == (PPDP_CONTEXT_ENTRY)(&m_PdpContextPendingList))
            {
            break;
            }
        else
            {
            pReturnEntry = pEntry;
            break;
            }
        }
    PdpContextUnlock();

    RETAILMSG(MSG_ON, (TEXT("RILNDIS: GetNextPdpContextEntry  pReturnEntry = %x \r\n"), pReturnEntry));
    return pReturnEntry;
}

BOOL            
CRilNDIS::IsPdpContextListEmpty( void )
{
    BOOL fReturn = IsListEmpty(&m_PdpContextPendingList);
    RETAILMSG(MSG_ON, (TEXT("RILNDIS: IsPdpContextListEmpty = %s \r\n"), 
            fReturn ? L"TRUE" : L"FALSE"));

    return fReturn;
}

void 
CRilNDIS::RemoveAndSignalAllPdpContextEntries ( DWORD ContextID )
{
    PPDP_CONTEXT_ENTRY pEntry;
    DWORD ResultDetails;
    BOOL    ExitNow = FALSE;

    PdpContextLock();
    //
    // Find entry with matching ContextID in our list of pending requests
    //
    while ( FALSE == ExitNow )
        {
        for (pEntry = (PPDP_CONTEXT_ENTRY)(m_PdpContextPendingList.Flink);
             TRUE;
             pEntry = (PPDP_CONTEXT_ENTRY)pEntry->Node.Flink)
            {
            if (pEntry == (PPDP_CONTEXT_ENTRY)(&m_PdpContextPendingList))
                {
                ExitNow = TRUE;
                break;
                }
            if ( pEntry->RilNdisGprsContext.dwContextID== ContextID )
                {
                ResultDetails = RIL_RESULT_ERROR;
                ((CRilInstanceHandle*)pEntry->pCRilInstance)->Notify(RIL_RESULT_ERROR,  pEntry->AsyncCommandID, &ResultDetails, sizeof(ResultDetails));
                RemoveEntryList(&pEntry->Node);
                InsertTailList(&m_PdpContextFreeList, &pEntry->Node);
                break;
                }
            }
        }
    PdpContextUnlock();

}

void            
CRilNDIS::ProcessNextContextRequest(void)
{
    PPDP_CONTEXT_ENTRY  pPdpContextEntry=NULL;
    RETAILMSG(MSG_ON, (TEXT("RILNDIS: ProcessNextContextRequest \r\n")));
    LockPdpContextList();
    pPdpContextEntry = GetNextPdpContextEntry();
    ASSERT(pPdpContextEntry);
    RemovePdpContextEntry(pPdpContextEntry);
    pPdpContextEntry = GetNextPdpContextEntry();
    while (pPdpContextEntry)
        { 
        ASSERT(pPdpContextEntry->AsyncCommandID);
        ASSERT(pPdpContextEntry->pCRilInstance);
        HRESULT Result=E_FAIL;  
        DWORD dwIndex;
        HANDLE hNdisChannelHandle = NULL;
        OEMNDISEVENT sEvent;
        memset(&sEvent, 0, sizeof(OEMNDISEVENT));

        RETAILMSG(MSG_ON, (TEXT("RILNDIS: ProcessNextContextRequest   cid = %x, Activation = %s pCRilInstance = %x AsyncCommandID = %x\r\n"), 
                pPdpContextEntry->RilNdisGprsContext.dwContextID, 
                pPdpContextEntry->RilNdisGprsContext.fContextActivation ? L"TRUE" : L"FALSE", 
                pPdpContextEntry->pCRilInstance,
                pPdpContextEntry->AsyncCommandID));

        if ( S_OK != NdisHandleFromCid( pPdpContextEntry->RilNdisGprsContext.dwContextID, &hNdisChannelHandle ))
            {
            hNdisChannelHandle = NULL; 
            }

       if ( !pPdpContextEntry->RilNdisGprsContext.fContextActivation )
            {
            if ( hNdisChannelHandle) 
                {
                sEvent.type = OEM_NDIS_CLOSE_CONNECTION;
                sEvent.event.open.cid = pPdpContextEntry->RilNdisGprsContext.dwContextID;
                NdisAllocateCommand ( OEM_NDIS_CLOSE_COMPLETE, hNdisChannelHandle, (LPVOID)pPdpContextEntry->pCRilInstance, pPdpContextEntry->AsyncCommandID, pPdpContextEntry->RilNdisGprsContext.dwContextID ); 
                if ( FALSE == NdisInterfaceTransmitEvent( hNdisChannelHandle, &sEvent) )
                    {
                    RETAILMSG(MSG_ON, (TEXT("RILNDIS: ProcessNextContextRequest  CLOSE handle = %x Call FAILED\r\n"), hNdisChannelHandle));
                    Result = E_FAIL;
                    NdisShutdownConnection ( hNdisChannelHandle );
                    }
                else
                    {
                    RETAILMSG(MSG_ON, (TEXT("RILNDIS: ProcessNextContextRequest  CLOSE handle = %x \r\n"), hNdisChannelHandle));
                    Result = S_OK;
                    }
                }
            else //  Error
                {
                Result = RIL_E_INVALIDCONTEXTSTATE; 
                }
            }
        else 
            {
            if ( NULL == hNdisChannelHandle) // This should normally be the case.
                {    
                // Check to see if there are any available to initialise.
                Sleep(1000);
                Result = NdisGetFreeNdisEntry( &dwIndex);
                if ( SUCCEEDED(Result) )
                    {
                    hNdisChannelHandle = NdisInterfaceInitialise (dwIndex);
                    if ( hNdisChannelHandle )
                        {
                        RETAILMSG(MSG_ON, (TEXT("RILNDIS: ProcessNextContextRequest  OPEN handle = %x \r\n"), hNdisChannelHandle));
                        NdisAddNdisEntry(pPdpContextEntry->RilNdisGprsContext.dwContextID,  dwIndex, hNdisChannelHandle, pPdpContextEntry->pCRilInstance, &pPdpContextEntry->RilNdisGprsContext ); 
                        NdisAllocateCommand ( OEM_NDIS_OPEN_COMPLETE, hNdisChannelHandle, (LPVOID)pPdpContextEntry->pCRilInstance, pPdpContextEntry->AsyncCommandID, pPdpContextEntry->RilNdisGprsContext.dwContextID);
                        NdisAddNotifyEvent(hNdisChannelHandle); 
                        }
                    else
                        {
                        RETAILMSG(MSG_ON, (TEXT("RILNDIS: ProcessNextContextRequest  OPEN FAILED \r\n")));
                        Result = E_FAIL;
                        }
                    }
                else
                    {
                    Result = RIL_E_MAXCONTEXTS;
                    }
                }  
             else // Error 
                {
                Result = RIL_E_INVALIDCONTEXTSTATE;
                }
            }

            // Process error condition
            if ( FAILED(Result) )
                {
                NdisRemoveCommand( pPdpContextEntry->AsyncCommandID );
                ((CRilInstanceHandle*)pPdpContextEntry->pCRilInstance)->Notify(RIL_RESULT_ERROR, 
                                                pPdpContextEntry->AsyncCommandID, 
                                                &Result, 
                                                sizeof(Result));
                RemovePdpContextEntry(pPdpContextEntry);
                pPdpContextEntry = GetNextPdpContextEntry();
                }
            else
                {
                pPdpContextEntry = NULL;
                }

        }
    UnlockPdpContextList();
}


CRilInstanceNDIS::CRilInstanceNDIS()
{
    m_pCrilInstance = NULL;
}

CRilInstanceNDIS::~CRilInstanceNDIS()
{
    m_pCrilInstance = NULL;
}

void CRilInstanceNDIS::Init(PVOID hInstance)
{
    m_pCrilInstance = hInstance;
}

// **************************************************************************
// Function Name: NdisSendPacket
// 
// Purpose:       Add async command and make NDIS call.
//
// Arguments:     IN ndis packet pointer
//
// Return Values:   E_FAIL if parameter or immediate failure detected.
//                  Command result value for async response.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HRESULT  CRilInstanceNDIS::NdisSendPacket( RILNDISPACKET* lpNdisPacket ) 
{
   HRESULT hr=E_FAIL ;
   CRilHandle* pCrilHandle = ((CRilInstanceHandle*)m_pCrilInstance)->GetCrilHandle();

   NdisPacketTest ( lpNdisPacket );
    
   if ( pCrilHandle )
   {
       HANDLE hNdisChannelHandle = NULL;
       OEMNDISEVENT sEvent;
       memset(&sEvent, 0, sizeof(OEMNDISEVENT));

   
       // Retrieve the handle from the Context ID. 
       if ( S_OK != pCrilHandle->CRilNdis.NdisHandleFromCid( lpNdisPacket->dwContextId, &hNdisChannelHandle ))
            hNdisChannelHandle = NULL;
        
       RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisSendPacket Packet = %x, ContextID = %x, NdisChannelHandle = %x \r\n"), lpNdisPacket, lpNdisPacket->dwContextId, hNdisChannelHandle));    
       if (hNdisChannelHandle)
       {
            sEvent.type = OEM_NDIS_TX_PACKET;
            sEvent.event.packet_p = lpNdisPacket; 
            
            DumpPacket(lpNdisPacket);

            if( pCrilHandle->CRilNdis.NdisInterfaceTransmitEvent( hNdisChannelHandle, &sEvent) )
            {
                hr = S_OK;
            }
        }
   }
    
   return hr;
}



// **************************************************************************
// Function Name: NdisSetGPRSContextActivated
// 
// Purpose:      Bring the GRPS context up or down depending on input.
//
// Arguments:    IN structure defining the connection details.
//
// Return Values:  E_FAIL if error detected.
//                 Command result for async response.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HRESULT  CRilInstanceNDIS::NdisSetGPRSContextActivated (const RILNDISGPRSCONTEXT *lpNdisSetGprsContextActivated )
{
    HRESULT Result=E_FAIL; 
    HRESULT AsyncCommandID = 0;
    CRilHandle* pCrilHandle = ((CRilInstanceHandle*)m_pCrilInstance)->GetCrilHandle();
 
    RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisSetGPRSContextActivated  cid = %x, Activation = %s \r\n"), 
             lpNdisSetGprsContextActivated->dwContextID, lpNdisSetGprsContextActivated->fContextActivation ? L"TRUE" : L"FALSE" ));
 
    if ( pCrilHandle && lpNdisSetGprsContextActivated && lpNdisSetGprsContextActivated->dwContextID)
        {
    
        DWORD dwIndex;
        HANDLE hNdisChannelHandle = NULL;
        OEMNDISEVENT sEvent;
        memset(&sEvent, 0, sizeof(OEMNDISEVENT));
        RILNDISGPRSCONTEXT NdisSetGprsContextActivated;
        CeSafeCopyMemory(&NdisSetGprsContextActivated, lpNdisSetGprsContextActivated, sizeof(NdisSetGprsContextActivated));


        // Force NULL-terminated string variables
        NdisSetGprsContextActivated.tszDNS1[ARRAY_LENGTH(NdisSetGprsContextActivated.tszDNS1)-1]= TCHAR ('\0');
        NdisSetGprsContextActivated.tszDNS2[ARRAY_LENGTH(NdisSetGprsContextActivated.tszDNS2)-1]= TCHAR ('\0');
        NdisSetGprsContextActivated.tszPassword[ARRAY_LENGTH(NdisSetGprsContextActivated.tszPassword)-1]= TCHAR ('\0');
        NdisSetGprsContextActivated.tszUserName[ARRAY_LENGTH(NdisSetGprsContextActivated.tszUserName)-1]= TCHAR ('\0');


        AsyncCommandID = pCrilHandle->GetNextCmdID();
        pCrilHandle->CRilNdis.LockPdpContextList();
        if ( FALSE == pCrilHandle->CRilNdis.IsPdpContextListEmpty())
            {
            RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisSetGPRSContextActivated  Queueing Request Command ID = %x \r\n"), AsyncCommandID));
            Result = S_OK;
            pCrilHandle->CRilNdis.AddPdpContextEntry(&NdisSetGprsContextActivated,AsyncCommandID,(PVOID)m_pCrilInstance);
            }
        else 
            {
            if ( S_OK != pCrilHandle->CRilNdis.NdisHandleFromCid( NdisSetGprsContextActivated.dwContextID, &hNdisChannelHandle ))
                {
                hNdisChannelHandle = NULL; 
                }

            if ( !NdisSetGprsContextActivated.fContextActivation ) // Deactivating
                {
                if ( hNdisChannelHandle) 
                    {
                    pCrilHandle->CRilNdis.NdisRemoveCallbacks(NdisSetGprsContextActivated.dwContextID);
                    sEvent.type = OEM_NDIS_CLOSE_CONNECTION;
                    sEvent.event.open.cid = NdisSetGprsContextActivated.dwContextID;
                    pCrilHandle->CRilNdis.NdisAllocateCommand ( OEM_NDIS_CLOSE_COMPLETE, hNdisChannelHandle, (LPVOID)m_pCrilInstance, AsyncCommandID, NdisSetGprsContextActivated.dwContextID ); 
                    if ( FALSE == pCrilHandle->CRilNdis.NdisInterfaceTransmitEvent( hNdisChannelHandle, &sEvent) )
                        {
                        RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisSetGPRSContextActivated  CLOSE handle = %x Call FAILED\r\n"), hNdisChannelHandle));
                        Result = E_FAIL;
                        pCrilHandle->CRilNdis.NdisShutdownConnection ( hNdisChannelHandle );
                        }
                    else
                        {
                        RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisSetGPRSContextActivated  CLOSE handle = %x \r\n"), hNdisChannelHandle));
                        Result = S_OK;
                        pCrilHandle->CRilNdis.AddPdpContextEntry(&NdisSetGprsContextActivated,AsyncCommandID,(PVOID)m_pCrilInstance);
                        }
                    }
                else //  Error
                    {
                    Result = RIL_E_INVALIDCONTEXTSTATE; 
                    }
                }
            else 
            /*  
            Activating:
            */  
                {
                if ( NULL == hNdisChannelHandle) // This should normally be the case.
                    {    
                    // Check to see if there are any available to initialise.
                    Result = pCrilHandle->CRilNdis.NdisGetFreeNdisEntry( &dwIndex);
                    if ( SUCCEEDED(Result) )
                        {
                        hNdisChannelHandle = pCrilHandle->CRilNdis.NdisInterfaceInitialise (dwIndex);
                        if ( hNdisChannelHandle )
                            {
                            RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisSetGPRSContextActivated  OPEN handle = %x \r\n"), hNdisChannelHandle));
                            pCrilHandle->CRilNdis.NdisAddNdisEntry(NdisSetGprsContextActivated.dwContextID,  dwIndex, hNdisChannelHandle, m_pCrilInstance, &NdisSetGprsContextActivated ); 
                            pCrilHandle->CRilNdis.NdisAllocateCommand ( OEM_NDIS_OPEN_COMPLETE, hNdisChannelHandle, (LPVOID)m_pCrilInstance, AsyncCommandID, NdisSetGprsContextActivated.dwContextID);
                            pCrilHandle->CRilNdis.NdisAddNotifyEvent(hNdisChannelHandle); 
                            pCrilHandle->CRilNdis.AddPdpContextEntry(&NdisSetGprsContextActivated,AsyncCommandID,(PVOID)m_pCrilInstance);
                            }
                        else
                            {
                            RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisSetGPRSContextActivated  OPEN FAILED \r\n")));
                            Result = E_FAIL;
                            }
                        }
                    else
                        {
                        Result = RIL_E_MAXCONTEXTS;
                        }
                    }  
                else // Error 
                    {
                    Result = RIL_E_INVALIDCONTEXTSTATE;
                    }
                }
            }
            pCrilHandle->CRilNdis.UnlockPdpContextList();
        }


    if ( FAILED(Result) && AsyncCommandID )
        {
            pCrilHandle->CRilNdis.NdisRemoveCommand( AsyncCommandID );
            AsyncCommandID = Result;
        }

    return AsyncCommandID;
}


// **************************************************************************
// Function Name: NdisReceivePacketDone
// 
// Purpose:      Notify the NDIS radio that the Rx packet has been consumed.
//
// Arguments:    IN Rx packet pointer
//
// Return Values:  E_FAIL if error detected.
//                 Command result for async response.
//
// Side effects:  
// 
// Description:   
// **************************************************************************
HRESULT CRilInstanceNDIS::NdisReceivePacketDone (RILNDISPACKET* lpNdisPacket)
{
    HRESULT hr = E_FAIL; 
    CRilHandle* pCrilHandle = ((CRilInstanceHandle*)m_pCrilInstance)->GetCrilHandle();
   
    if ( pCrilHandle && lpNdisPacket )
    {
        OEMNDISEVENT sEvent;
        HANDLE       hNdisChannelHandle = NULL;
        memset(&sEvent, 0, sizeof(OEMNDISEVENT));
        
        ULONG lpMapped = UnMapPtr((LPVOID)lpNdisPacket);

        // Retrieve the handle from the Context ID.
        if ( S_OK != pCrilHandle->CRilNdis.NdisHandleFromCid( lpNdisPacket->dwContextId, &hNdisChannelHandle ))
            hNdisChannelHandle = NULL;

        RETAILMSG(MSG_ON, (TEXT("RILNDIS: NdisReceivePacketDone Packet = %x, ContextID = %x, NdisChannelHandle = %x \r\n"), lpNdisPacket, lpNdisPacket->dwContextId, hNdisChannelHandle));    

        sEvent.type = OEM_NDIS_RX_PACKET_COMPLETE;
        if (lpMapped)
            sEvent.event.packet_p = (RILNDISPACKET*)lpMapped;
        else
            sEvent.event.packet_p = lpNdisPacket;
            
        if (hNdisChannelHandle)
        {
            if ( pCrilHandle->CRilNdis.NdisInterfaceTransmitEvent( hNdisChannelHandle, &sEvent) )
            {
                hr = S_OK;
            }
        }
    }
    

     return hr;
     
}

// **************************************************************************
// Function Name: AsyncResponse
// 
// Purpose:       Initialization
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
AsyncResponse::AsyncResponse()
{
    InitializeCriticalSection(&m_cs);
    InitializeListHead(&m_AsyncPendingList);
    InitializeListHead(&m_AsyncFreeList);
}


// **************************************************************************
// Function Name:  ~AsyncResponse
// 
// Purpose:         Clean up.
//
// Arguments: 
//
// Return Values:  
//
// Side effects:  
// 
// Description:   
// **************************************************************************
AsyncResponse::~AsyncResponse()
{
    PASYNCCOMMAND pEntry;
    BOOL ExitNow = FALSE;
    //
    // Find entry with matching ContextID in our list of pending requests
    //
    AsyncListLock();
    while ( FALSE == ExitNow )
        {
        for (pEntry = (PASYNCCOMMAND)(m_AsyncPendingList.Flink);
             TRUE;
             pEntry = (PASYNCCOMMAND)pEntry->Node.Flink)
            {
            if (pEntry == (PASYNCCOMMAND)(&m_AsyncPendingList))
                {
                ExitNow = TRUE;
                break;
                }

            RemoveEntryList(&pEntry->Node);
            InsertTailList(&m_AsyncFreeList, &pEntry->Node);
            break;
            }
        }
    ExitNow = FALSE;
    while ( FALSE == ExitNow )
        {
        for (pEntry = (PASYNCCOMMAND)(m_AsyncFreeList.Flink);
             TRUE;
             pEntry = (PASYNCCOMMAND)pEntry->Node.Flink)
            {
            if (pEntry == (PASYNCCOMMAND)(&m_AsyncFreeList))
                {
                ExitNow = TRUE;
                break;
                }
            RemoveEntryList(&pEntry->Node);
            LocalFree ( pEntry );
            break;
            }
        }
    AsyncListUnlock();
}


void 
AsyncResponse::AddCommand ( OEMNDISAPIREFERENCE *lpOemNdisApiReference )
{

    PASYNCCOMMAND pEntry = NULL;

    if ( lpOemNdisApiReference )
        {
        AsyncListLock();
        if (!IsListEmpty(&m_AsyncFreeList))
            {
            pEntry = (PASYNCCOMMAND)RemoveHeadList(&m_AsyncFreeList);
            }

        if (pEntry == NULL)
            {
            pEntry = (PASYNCCOMMAND)LocalAlloc(LPTR, sizeof(*pEntry));
            }

        if ( pEntry )
            {
            memcpy(&pEntry->OemNdisApiReference, lpOemNdisApiReference, sizeof(OEMNDISAPIREFERENCE));
            }
        InsertTailList(&m_AsyncPendingList, &pEntry->Node);
        AsyncListUnlock();
        }
}

void 
AsyncResponse::RemoveCommand( HRESULT hrCommandID )
{
    if ( hrCommandID )
        {
        PASYNCCOMMAND pEntry;
  
        AsyncListLock();
        for (pEntry = (PASYNCCOMMAND)(m_AsyncPendingList.Flink);
             TRUE;
             pEntry = (PASYNCCOMMAND)pEntry->Node.Flink)
            {
            if (pEntry == (PASYNCCOMMAND)(&m_AsyncPendingList))
                {
                RETAILMSG(MSG_ON, (TEXT("RILNDIS: RemoveCommand  hrCommandID = %x NOT FOUND\r\n"), hrCommandID));
                break;
                }

            if ( pEntry->OemNdisApiReference.hrCommandID == hrCommandID )
                {
                RemoveEntryList(&pEntry->Node);
                InsertTailList(&m_AsyncFreeList, &pEntry->Node);
                break;
                }
            }
        AsyncListUnlock();
        }
}

BOOL
AsyncResponse::ProcessCommand ( const OEMNDISAPIREFERENCE *lpOemNdisApiReference )
{
   
    BOOL fReturn = FALSE;
    if ( lpOemNdisApiReference )
        {
        CRilInstanceHandle* hCrilInstance=NULL;
        PASYNCCOMMAND pEntry;
      

        AsyncListLock();  
        for (pEntry = (PASYNCCOMMAND)(m_AsyncPendingList.Flink);
             TRUE;
             pEntry = (PASYNCCOMMAND)pEntry->Node.Flink)
            {

            fReturn = TRUE;

            if (pEntry == (PASYNCCOMMAND)(&m_AsyncPendingList))
            {
            RETAILMSG(MSG_ON, (TEXT("RILNDIS: ProcessCommand Notify FAILED \r\n")));
            fReturn = FALSE;
            break;
            }

            if ( lpOemNdisApiReference->eEventType )
                {
                if ( (lpOemNdisApiReference->eEventType == pEntry->OemNdisApiReference.eEventType) &&
                    (lpOemNdisApiReference->hNdisHandle == pEntry->OemNdisApiReference.hNdisHandle))
                    {
                    hCrilInstance = (CRilInstanceHandle*)pEntry->OemNdisApiReference.dwCrilInstance;
                    if ( hCrilInstance )
                        {
                        DWORD dwResult = lpOemNdisApiReference->dwStatus ? RIL_RESULT_ERROR : RIL_RESULT_OK;
                        hCrilInstance->Notify(  dwResult, 
                                    pEntry->OemNdisApiReference.hrCommandID, 
                                    &lpOemNdisApiReference->RilNdisGprsContextResponse, 
									sizeof(lpOemNdisApiReference->RilNdisGprsContextResponse));
                        RETAILMSG(MSG_ON, (TEXT("RILNDIS: ProcessCommand Notify Command Status = %x Comamand ID = %0x \r\n"), 
                                    dwResult, 
                                    pEntry->OemNdisApiReference.hrCommandID));
                        }
                    RemoveEntryList(&pEntry->Node);
                    InsertTailList(&m_AsyncFreeList, &pEntry->Node);
                    break;
                    }
                }
            }
        AsyncListUnlock();
        }
    return fReturn;
}

void 
AsyncResponse::RemoveAllCommands( HANDLE hContextHandle )
{
    PASYNCCOMMAND pEntry;
    BOOL ExitNow = FALSE;
    //
    // Find entry with matching ContextID in our list of pending requests
    //
    AsyncListLock();
    while ( FALSE == ExitNow )
        {
        for (pEntry = (PASYNCCOMMAND)(m_AsyncPendingList.Flink);
             TRUE;
             pEntry = (PASYNCCOMMAND)pEntry->Node.Flink)
            {
            if (pEntry == (PASYNCCOMMAND)(&m_AsyncPendingList))
                {
                ExitNow = TRUE;
                break;
                }
            if ( hContextHandle == pEntry->OemNdisApiReference.hNdisHandle )
                {
                RemoveEntryList(&pEntry->Node);
                InsertTailList(&m_AsyncFreeList, &pEntry->Node);
                break;
                }
            }
        }
    AsyncListUnlock();
}

inline void
AsyncResponse::AsyncListLock()
{
    EnterCriticalSection(&m_cs);
}

inline void
AsyncResponse::AsyncListUnlock()
{
    LeaveCriticalSection(&m_cs);
}

#endif 
